package com.zj.docker.service.impl;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.UnmodifiableIterator;
import com.spotify.docker.client.messages.*;
import com.zj.docker.Repository.ContainersRepository;
import com.zj.docker.Repository.ImagesInfoRepository;
import com.zj.docker.Repository.VolumesRepository;
import com.zj.docker.client.LogClient;
import com.zj.docker.common.MqTask;
import com.zj.docker.component.DockerComponent;
import com.zj.docker.component.DockerPortComponent;
import com.zj.docker.component.LogComponent;
import com.zj.docker.domain.Containers;
import com.zj.docker.domain.ImagesInfo;
import com.zj.docker.domain.Volumes;
import com.zj.docker.enums.ContainerStatusEnum;
import com.zj.docker.enums.LogEnum;
import com.zj.docker.rabbitmq.Sender;
import com.zj.docker.service.ContainersService;
import com.zj.docker.service.ImagesInfoService;
import com.zj.docker.utils.CollectionUtils;
import com.zj.docker.utils.JsonUtils;
import com.zj.docker.utils.RandomUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.origin.SystemEnvironmentOrigin;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.persistence.Column;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * @Auther: zj
 * @Date: 2018/12/11 11:29
 * @Description:
 */
@Service
@Slf4j
public class ContainersServiceImpl implements ContainersService{
    @Autowired
    ImagesInfoService imagesInfoService;
    @Autowired
    ImagesInfoRepository imagesInfoRepository;
    @Autowired
    DockerPortComponent dockerPortComponent;
    @Autowired
    DockerComponent docker;
    @Autowired
    VolumesRepository volumesRepository;
    @Autowired
    ContainersRepository containersRepository;
    @Autowired
    Sender sender;
    @Autowired
    LogComponent logComponent;
    @Autowired
    LogClient logClient;

    @Override
    public void saveOrUpdata(Containers containers) {
        containersRepository.save( containers );
    }

    @Override
    public void delete(Containers containers) {
        containersRepository.delete( containers );
    }


    @Async("taskExecutor")
    @Override
    public void createContainer(String author, String imageId, String[] cmd, ImmutableSet<String> exportPorts, String containerName, String[] env, String[] destination, String time, String sessionId,
                                String method,
                                String url,
                                String ip,
                                String userAgent) {

        MqTask mqTask = new MqTask();
        mqTask.setUid( sessionId );

        //校验image
        ImagesInfo image = imagesInfoRepository.findOneByImageId( imageId );
        if (image==null) {
            mqTask.setData( "该镜像在数据库中不存在！" );
            sender.send( JsonUtils.objectToJson( mqTask  ));
        }
        if (!Objects.equals( image.getAuthorName(), author )&&image.getHasOpen()!=1) {//验证该镜像不是当前用户的私有镜像，同时不对外开放
            mqTask.setData( "该镜像您无权使用！" );
            sender.send( JsonUtils.objectToJson( mqTask  ) );
        }
        Containers container = new Containers();
        HostConfig hostConfig;
        ContainerConfig.Builder builder = ContainerConfig.builder();

        // 设置暴露端口
        if(exportPorts != null) {
            // 宿主机端口与暴露端口绑定
            Set<String> realExportPorts = new HashSet<>();
            Map<String, List<PortBinding>> portBindings = new HashMap<>(16);
            UnmodifiableIterator<String> iterator = exportPorts.iterator();

            // 去除/tcp后的端口集合
            while(iterator.hasNext()) {
                // 取出暴露端口号,形如：80/tcp
                String exportPort = iterator.next();
                // 取出去除/tcp后的端口号
                String tmp = exportPort.trim().split("/")[0];
                realExportPorts.add(tmp);

                // 捆绑端口
                List<PortBinding> hostPorts = new ArrayList<>();
                // 随机分配主机端口
                Integer hostPort = dockerPortComponent.randomPort();
                hostPorts.add(PortBinding.of("0.0.0.0", hostPort));
                portBindings.put(exportPort, hostPorts);
            }

            container.setPort( JsonUtils.objectToJson(portBindings));

            builder.exposedPorts(realExportPorts);

            hostConfig = HostConfig.builder()
                    .portBindings(portBindings)
                    .build();
        } else {
            hostConfig = HostConfig.builder().build();
        }

        // 构建ContainerConfig
        builder.hostConfig(hostConfig);
        builder.image(image.getFullName());
        builder.tty(true);
        if(CollectionUtils.isNotArrayBlank(cmd)) {
            builder.cmd(cmd);
            container.setCommand(Arrays.toString(cmd));
        }

        if(CollectionUtils.isNotArrayBlank(destination)) {
            builder.volumes(destination);
        }
        if(CollectionUtils.isNotArrayBlank(env)) {
            builder.env(env);
            container.setEnv(Arrays.toString(env));
        }
        ContainerConfig containerConfig = builder.build();

        try {
            ContainerCreation creation = docker.getThisDockerClient().createContainer(containerConfig);

            container.setContainerId(creation.id());
            container.setContainerName(containerName);// 仅存在于数据库，不代表实际容器名
            container.setImageName(image.getFullName());

            if(CollectionUtils.isNotArrayBlank(destination)) {
                // 为数据库中的sysvolumes插入
                ImmutableList<ContainerMount> info = docker.getThisDockerClient().inspectContainer(creation.id()).mounts();
                for(int i = 0;i<destination.length;i++){
                    Volumes Volume = new Volumes();
                    Volume.setContainerId(creation.id());
                    Volume.setDestination(destination[i]);
                    Volume.setName(info.get(i).name());
                    Volume.setSource(info.get(i).source());
                    Volume.setUpdateDate( time );
                    Volume.setCreateDate( time );
                    volumesRepository.save( Volume );
                }
            }

            // 设置状态
            ContainerStatusEnum status = getStatus(creation.id());
            if(status == null) {
                mqTask.setData( "读取容器状态异常!" );
                sender.send(JsonUtils.objectToJson( mqTask  ));
            }
            container.setStatus(status.getCode());
            container.setAuthor( author );
            container.setCreateDate(time);
            container.setUpdateDate( time );

            containersRepository.save( container );
//            // 写入日志
            logComponent.saveLogAsync( author,url,method,ip,userAgent,LogEnum.DOCKER_CREATE);

            mqTask.setData( "创建容器成功!" );
            sender.send(JsonUtils.objectToJson( mqTask  ));

        } catch (Exception e) {
            logComponent.saveLogAsync( author,url,method,ip,userAgent,LogEnum.DOCKER_CREATE_ERROR,e );
            log.error("创建容器出现异常，异常位置：{}，错误信息：{}", "ContainersServiceImpl.createContainer()",e.getMessage());

        }

    }

    @Override
    public ContainerStatusEnum getStatus(String containerId) {
        try {
            ContainerInfo info = docker.getThisDockerClient().inspectContainer(containerId);
            ContainerState state = info.state();

            if(state.running()) {
                if(state.paused()) {
                    return ContainerStatusEnum.PAUSE;
                } else {
                    return ContainerStatusEnum.START;
                }
            } else {
                return ContainerStatusEnum.STOP;
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("获取容器状态出现异常，异常位置：{}，错误信息：{}","ContainersServiceImpl.getStatus()",e.getMessage());
            return null;
        }
    }

    @Override
    public Page<Containers> listAllContainerByAuthorWithPage(String author, Integer pageIndex) {
        Pageable pageable = PageRequest.of( pageIndex,5);
        Page<Containers> containers= containersRepository.findAllByAuthor( author,pageable );
        return containers;
    }

    @Override
    public Containers findOneByContainerId(String containerId) {
        Containers container = containersRepository.findOneByContainerId(containerId);
        return container;
    }

    @Override
    public List<Containers> listAllContainer(String author) {
        List<Containers> list = containersRepository.findAllByAuthor( author );
        return list;
    }

    @Override
    public List<Containers> listAllContainer() {
        return containersRepository.findAll();
    }

}
